home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
Pakiet bezpieczenstwa
/
mini Pentoo LiveCD 2006.1
/
mpentoo-2006.1.iso
/
livecd.squashfs
/
usr
/
lib
/
perl5
/
5.8.7
/
File
/
Path.pm
< prev
next >
Wrap
Text File
|
2006-04-25
|
8KB
|
311 lines
package File::Path;
=head1 NAME
File::Path - create or remove directory trees
=head1 SYNOPSIS
use File::Path;
mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711);
rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1);
=head1 DESCRIPTION
The C<mkpath> function provides a convenient way to create directories, even
if your C<mkdir> kernel call won't create more than one level of directory at
a time. C<mkpath> takes three arguments:
=over 4
=item *
the name of the path to create, or a reference
to a list of paths to create,
=item *
a boolean value, which if FALSE (the default for non-root users) will
cause C<rmtree> to adjust the mode of directories (if required) prior
to attempting to remove the contents. Note that on interruption or
failure of C<rmtree>, directories may be left with more permissi
modes for the owner.
=item *
the numeric mode to use when creating the directories
(defaults to 0777), to be modified by the current umask.
=back
It returns a list of all directories (including intermediates, determined
using the Unix '/' separator) created.
If a system error prevents a directory from being created, then the
C<mkpath> function throws a fatal error with C<Carp::croak>. This error
can be trapped with an C<eval> block:
eval { mkpath($dir) };
if ($@) {
print "Couldn't create $dir: $@";
}
Similarly, the C<rmtree> function provides a convenient way to delete a
subtree from the directory structure, much like the Unix command C<rm -r>.
C<rmtree> takes three arguments:
=over 4
=item *
the root of the subtree to delete, or a reference to
a list of roots. All of the files and directories
below each root, as well as the roots themselves,
will be deleted.
=item *
a boolean value, which if TRUE will cause C<rmtree> to
print a message each time it examines a file, giving the
name of the file, and indicating whether it's using C<rmdir>
or C<unlink> to remove it, or that it's skipping it.
(defaults to FALSE)
=item *
a boolean value, which if TRUE will cause C<rmtree> to
skip any files to which you do not have delete access
(if running under VMS) or write access (if running
under another OS). This will change in the future when
a criterion for 'delete permission' under OSs other
than VMS is settled. (defaults to FALSE)
=back
It returns the number of files successfully deleted. Symlinks are
simply deleted and not followed.
B<NOTE:> There are race conditions internal to the implementation of
C<rmtree> making it unsafe to use on directory trees which may be
altered or moved while C<rmtree> is running, and in particular on any
directory trees with any path components or subdirectories potentially
writable by untrusted users.
Additionally, if the third parameter is not TRUE and C<rmtree> is
interrupted, it may leave files and directories with permissions altered
to allow deletion (and older versions of this module would even set
files and directories to world-read/writable!)
Note also that the occurrence of errors in C<rmtree> can be determined I<only>
by trapping diagnostic messages using C<$SIG{__WARN__}>; it is not apparent
from the return value.
=head1 DIAGNOSTICS
=over 4
=item *
On Windows, if C<mkpath> gives you the warning: B<No such file or
directory>, this may mean that you've exceeded your filesystem's
maximum path length.
=back
=head1 AUTHORS
Tim Bunce <F<Tim.Bunce@ig.co.uk>> and
Charles Bailey <F<bailey@newman.upenn.edu>>
=cut
use 5.006;
use Carp;
use File::Basename ();
use Exporter ();
use strict;
use warnings;
use Cwd 'getcwd';
our $VERSION = "1.07";
our @ISA = qw( Exporter );
our @EXPORT = qw( mkpath rmtree );
my $Is_VMS = $^O eq 'VMS';
my $Is_MacOS = $^O eq 'MacOS';
# These OSes complain if you want to remove a file that you have no
# write permission to:
my $force_writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
$^O eq 'amigaos' || $^O eq 'MacOS' || $^O eq 'epoc');
sub mkpath {
my($paths, $verbose, $mode) = @_;
# $paths -- either a path string or ref to list of paths
# $verbose -- optional print "mkdir $path" for each directory created
# $mode -- optional permissions, defaults to 0777
local($")=$Is_MacOS ? ":" : "/";
$mode = 0777 unless defined($mode);
$paths = [$paths] unless ref $paths;
my(@created,$path);
foreach $path (@$paths) {
$path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT
# Logic wants Unix paths, so go with the flow.
if ($Is_VMS) {
next if $path eq '/';
$path = VMS::Filespec::unixify($path);
if ($path =~ m:^(/[^/]+)/?\z:) {
$path = $1.'/000000';
}
}
next if -d $path;
my $parent = File::Basename::dirname($path);
unless (-d $parent or $path eq $parent) {
push(@created,mkpath($parent, $verbose, $mode));
}
print "mkdir $path\n" if $verbose;
unless (mkdir($path,$mode)) {
my $e = $!;
# allow for another process to have created it meanwhile
croak "mkdir $path: $e" unless -d $path;
}
push(@created, $path);
}
@created;
}
sub _rmtree;
sub _rmtree
{
my ($path, $prefix, $up, $up_dev, $up_ino, $verbose, $safe) = @_;
my ($dev, $ino) = lstat $path or do {
carp "Can't stat $prefix$path ($!)" unless $!{ENOENT};
return 0;
};
unless (-d _)
{
print "unlink $prefix$path\n" if $verbose;
unless (unlink $path)
{
carp "Can't remove file $prefix$path ($!)";
return 0;
}
return 1;
}
unless (chdir $path)
{
carp "Can't chdir to $prefix$path ($!)";
return 0;
}
# avoid a race condition where a directory may be replaced by a
# symlink between the lstat and the chdir
my ($new_dev, $new_ino, $perm) = stat '.';
unless ("$new_dev:$new_ino" eq "$dev:$ino")
{
croak "Directory $prefix$path changed before chdir, aborting";
}
$perm &= 07777;
my $nperm = $perm | 0700;
unless ($safe or $nperm == $perm or chmod $nperm, '.')
{
carp "Can't make directory $prefix$path read+writeable ($!)";
$nperm = $perm;
}
my $count = 0;
if (opendir my $dir, '.')
{
my $entry;
while (defined ($entry = readdir $dir))
{
next if $entry =~ /^\.\.?$/;
$entry =~ /^(.*)$/s; $entry = $1; # untaint
$count += _rmtree $entry, "$prefix$path/", '..', $dev, $ino,
$verbose, $safe;
}
closedir $dir;
}
# restore directory permissions if required (in case the rmdir
# below fails) now, while we're still in the directory and may do
# so without a race via '.'
unless ($nperm == $perm or chmod $perm, '.')
{
carp "Can't restore permissions on directory $prefix$path ($!)";
}
# don't leave the caller in an unexpected directory
unless (chdir $up)
{
croak "Can't return to $up from $prefix$path ($!)";
}
# ensure that a chdir .. didn't take us somewhere other than
# where we expected (see CVE-2002-0435)
unless (($new_dev, $new_ino) = stat '.'
and "$new_dev:$new_ino" eq "$up_dev:$up_ino")
{
croak "Previous directory $up changed since entering $prefix$path";
}
print "rmdir $prefix$path\n" if $verbose;
if (rmdir $path)
{
$count++;
}
else
{
carp "Can't remove directory $prefix$path ($!)";
}
return $count;
}
sub rmtree
{
my ($p, $verbose, $safe) = @_;
$p = [] unless defined $p and length $p;
$p = [ $p ] unless ref $p;
my @paths = grep defined && length, @$p;
# default to "unsafe" for non-root (will chmod dirs)
$safe = $> ? 0 : 1 unless defined $safe;
unless (@paths)
{
carp "No root path(s) specified";
return;
}
my $oldpwd = getcwd or do {
carp "Can't fetch initial working directory";
return;
};
my ($dev, $ino) = stat '.' or do {
carp "Can't stat initial working directory";
return;
};
# untaint
for ($oldpwd) { /^(.*)$/s; $_ = $1 }
my $count = 0;
for my $path (@paths)
{
$count += _rmtree $path, '', $oldpwd, $dev, $ino, $verbose, $safe;
}
$count;
}
1;